package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParam;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private RestHighLevelClient client;

    /**
     * 全检检索酒店数据
     * @param param
     * @return
     */
    @Override
    public PageResult search(RequestParam param) {
        //1. 创建请求对象
        SearchRequest request = new SearchRequest("hotel");
        //2. 设置请求参数

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //2.1 设置查询条件

        //组合查询条件：最基础的查询（查全部 或 全文检索查询）

        //如果查询关键词为空，则设置为查询全部
        if(StringUtils.isBlank(param.getKey())) {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());

        } else {
            //如果查询关键词非空，则设置为全文检索查询
            boolQueryBuilder.must(QueryBuilders.matchQuery("all",param.getKey()));
        }

        //组合查询条件：动态添加品牌精确查询
        if(StringUtils.isNotBlank(param.getBrand())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand",param.getBrand()));
        }

        //组合查询条件：动态添加城市精确查询
        if(StringUtils.isNotBlank(param.getCity())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("city",param.getCity()));
        }

        //组合查询条件：动态添加星级精确查询
        if(StringUtils.isNotBlank(param.getStarName())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("starName",param.getStarName()));
        }

        //组合查询条件：动态添加价格范围查询

        //价格大于最小值
        if(param.getMinPrice()!=null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(param.getMinPrice()));
        }

         //价格小于最大值
        if(param.getMaxPrice()!=null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(param.getMaxPrice()));
        }

        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder,//boolQueryBuilder-原始查询条件
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD",true), //termQuery - 筛选条件
                        ScoreFunctionBuilders.weightFactorFunction(10)) //算分函数权重值
        });
        functionScoreQueryBuilder.boostMode(CombineFunction.SUM);//加权模式：求和

        //算分函数查询条件
        request.source().query(functionScoreQueryBuilder);


        //2.2 设置结果要求

        //分页设置
        int from = (param.getPage()-1) * param.getSize();
        request.source().from(from).size(param.getSize());

        //排序设置：所有酒店数据到我的定位坐标由近及远排序
        if(StringUtils.isNotBlank(param.getLocation())) {
            request.source().sort(SortBuilders
                    .geoDistanceSort("location",
                            new GeoPoint(param.getLocation()//我的定位坐标
                            ))
                    .order(SortOrder.ASC) //由近及远排序
                    .unit(DistanceUnit.KILOMETERS) //单位：千米
            );
        }


        //3. 执行搜索请求
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);


            //4. 解析搜索响应结果（从外到内，逐层解析）
            SearchHits hits = response.getHits();//命中的一级对象
            long total = hits.getTotalHits().value;//命中的总记录数
            SearchHit[] searchHits = hits.getHits();//命中的二级对象数组

            List<HotelDoc> hotels = new ArrayList<>();
            if(searchHits!=null && searchHits.length>0) {


                //遍历得到搜索命中的每个文档对象
                for (SearchHit hit : searchHits) {
                    //获取文档的JSON字符串
                    String hotelJSON = hit.getSourceAsString();
                    HotelDoc hotelDoc = JSON.parseObject(hotelJSON, HotelDoc.class);

                    Object[] sortValues = hit.getSortValues();
                    if(sortValues!=null && sortValues.length>0) {
                        Object distance = sortValues[0];
                        hotelDoc.setDistance(distance); //酒店到我的定位的距离
                    }

                    hotels.add(hotelDoc);
                }
            }
            return PageResult.of(total,hotels);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
